<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
    width: 100px;
    height: 100px;
    background-color: red;
    position: absolute;
    left: 588px;
    top: 50px;
}
.box2{
    width: 100px;
    height: 100px;
    background-color: blue;
    position: absolute;
    left: 10px;
    top: 200px;
}
.line{
    width: 1px;
    height: 500px;
    position: absolute;
    left: 588px;
    top: 0;
    background-color: blue;
}
</style>
</head>
<body>
    
<button class="btn">走你</button>
<div class="box">box</div>
<div class="box2">box2</div>
<span class="line"></span>

<button class="stop">停止</button>

<script>
// 运动
// 运动原理：持续改变元素的某个属性（计时器）
// 运动速度：每次改变的量
// 运动停止：到达目标位置并清除计时器

// 缓冲运动：
// 运动的速度持续改变
// 速度：每次改变的量是固定的
// 频率：计时器时间间隔

var btn = document.querySelector('.btn');
var box = document.querySelector('.box');
var box2 = document.querySelector('.box2');
var stop = document.querySelector('.stop');

// var timer = null;// 记录计时器id
// var speed = 5;// 运动速度
// var current = 10;// 当前位置
// var target = 588;// 目标位置

// 屏幕刷新率：60hz -> 每秒刷新60次
// 看起来比较顺畅的运动：每秒最少24帧
// 1秒=1000毫秒
// 1000/60 = 16.666667毫秒
// 1000/24 = 41.66667毫秒
// 所以，重复计时器的时间间隔为 16-41 毫秒

btn.onclick = function (){
    // animate(box,'left',10);
    // animate(box,'opacity',0.1);
    animate(box,'width',450);

    // 需求：box2运动目标位置之后，再执行'后续程序'
    animate(box2,'left',1150,function (){
        console.log( '后续程序' );
    });

}

stop.onclick = function (){
    // 停止box2的运动
    clearInterval(box2.timer);
}

/**
 * 运动函数：animate
 * 
 * 参数说明：
 * dom 要运动的元素节点
 * attr 要运动的属性
 * target 运动的目标位置
 * callback 运动完成之后执行的回调函数，可选参数
 * 
 * 功能说明：
 * 1.支持带px单位的属性运动（left top right bottom width margin...）
 * 2.支持滚动条属性运动（scrollLeft scrollTop）
 * 3.支持透明度属性运动（opacity）
 */
function animate(dom,attr,target,callback){
    // 根据不同属性获取当前值
    if (attr === 'opacity') {
        // 当前值和目标位置乘以100
        var current = getComputedStyle(dom)[attr] * 100;
        target = target * 100;
    }
    else if (attr === 'scrollLeft' || attr === 'scrollTop'){
        var current = dom[attr];
    }
    else {
        // 其他带px单位的属性
        var current = parseInt( getComputedStyle(dom)[attr] );// '50px' -> 50
    }

    // 启动计时器之前先清除
    clearInterval(dom.timer);
    dom.timer = setInterval(function (){
        // 生成持续变化的速度
        var speed = (target-current)/10;
        // 对speed取整（正数向上取整，负数向下取整）
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

        // 当前值累加speed
        current += speed;

        // 判断是否到达目标位置（剩余运动量<=每次的运动量）
        if (Math.abs(target-current) <= Math.abs(speed)) {
            // 到达目标位置
            if (attr === 'opacity') {
                dom.style[attr] = target/100;
            }
            else if (attr === 'scrollLeft' || attr === 'scrollTop'){
                dom[attr] = target;
            }
            else {
                dom.style[attr] = target + 'px';
            }
            // 清除计时器
            clearInterval(dom.timer);
            console.log( '到达目标位置。。。' );
            typeof callback === 'function' && callback();
        } else {
            // 未到达目标位置
            if (attr === 'opacity') {
                dom.style[attr] = current/100;
            }
            else if (attr === 'scrollLeft' || attr === 'scrollTop'){
                dom[attr] = current;
            }
            else {
                dom.style[attr] = current + 'px';
            }
        }
    },20);
}

</script>
</body>
</html>